/** 
 * @class Ext.data.DwrProxy
 * @extends Ext.data.DataProxy
 * <p>DwrProxy使用{@link #dwrFn}与服务端交互, 而不是像{@link Ext.data.HttpProxy HttpProxy}一样设置
 * {@link Ext.data.HttpProxy#url url}, dwrFn必须为function. </p>
 * <p>如果不配置dwrFn, 那么DwrProxy使用{@link #api api}配置进行CRUD操作, {@link #api api}的配置属性必须是一个function.</p>
 * <p><b>Example</b>: <pre><code>
var proxy = new Ext.data.DwrProxy({
    dwrFn : User.read,
    
    // 如果未定义api则read方法使用dwrFn, 否则使用api中的read方法
    api: {
        read: User.read,
        create: User.create,
        udpate: User.update,
        destroy: User.remove
    }
});
 * </code></pre></p>
 */
Ext.data.DwrProxy = function(conn) {
    Ext.applyIf(conn, {
        url: "#"
    });
    Ext.data.DwrProxy.superclass.constructor.call(this, conn);
    
    /**
     * @property dwrFn
     * The read function of dwr.
     * @type Function
     */
    this.dwrFn = conn.dwrFn || Ext.emptyFn;
    
    this.timeout = conn.timeout || this.timeout;
};

Ext.extend(Ext.data.DwrProxy, Ext.data.DataProxy, {
    /**
     * @property timeout
     * The timeout in milliseconds to be used for dwr requests. (defaults to 30000)
     * @type Number
     */
    timeout: 30000,
    
    /**
     * DwrProxy implementation of DataProxy#doRequest
     * @param {String} action The crud action type (create, read, update, destroy)
     * @param {Ext.data.Record/Ext.data.Record[]} rs If action is load, rs will be null
     * @param {Object} params An object containing properties which are to be used as HTTP parameters
     * for the request to the remote server.
     * @param {Ext.data.DataReader} reader The Reader object which converts the data
     * object into a block of Ext.data.Records.
     * @param {Function} callback
     * <div class="sub-desc"><p>A function to be called after the request.
     * The <tt>callback</tt> is passed the following arguments:<ul>
     * <li><tt>r</tt> : Ext.data.Record[] The block of Ext.data.Records.</li>
     * <li><tt>options</tt>: Options object from the action request</li>
     * <li><tt>success</tt>: Boolean success indicator</li></ul></p></div>
     * @param {Object} scope The scope (<code>this</code> reference) in which the callback function is executed. Defaults to the browser window.
     * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
     */
    doRequest: function(action, rs, params, reader, callback, scope, options) {
        // While this action has no dwr function fn is dwrFn
        var fn = Ext.isFunction(this.api[action]) ? this.api[action] : this.dwrFn;
        
        var o = {
            request: {
                callback: callback,
                scope: scope,
                arg: options,
                rs: rs
            },
            reader: reader
        };
        
        // The parameters to the dwr function.  
        var args = [];
        if (action === Ext.data.Api.actions.read) {
            args.push(this.createCallback(action, rs, o));
        } else {
            args.push(params.data);
            args.push(this.createCallback(action, rs, o));
        }
        
        try {
            fn.apply(window, args);
        } catch (e) {
            this.fireEvent('exception', this, 'dwrexception', action, o, null, e);
        }
    },
    
    // private
    createCallback: function(action, rs, o) {
        var self = this;
        var callback = function(response) {
            if (action === Ext.data.Api.actions.read) {
                self.onRead(action, o, response);
            } else {
                self.onWrite(action, o, response, rs);
            }
        };
        var errorHandler = function(message, e) {
            if (action === Ext.data.Api.actions.read) {
                // @deprecated: fire loadexception for backwards compat.
                // TODO remove
                self.fireEvent('loadexception', self, o, null, e);
            }
            self.fireEvent('exception', self, 'response', action, o, null, e);
            o.request.callback.call(o.request.scope, {}, null, false);
        };
        
        return {
            callback: callback, // Callback for dwr Ajax request.
            errorHandler: errorHandler, // Dwr Ajax request error handler.
            timeout: this.timeout,
            scope: this
        }
    },
    
    /**
     * Callback for read action
     * @param {String} action Action name as per {@link Ext.data.Api.actions#read}.
     * @param {Object} o The request transaction object
     * @param {Object} res The server response
     */
    onRead: function(action, o, response) {
        var res;
        try {
            res = o.reader.readRecords(response);
        } catch (e) {
            // @deprecated: fire old loadexception for backwards-compat.
            // TODO remove
            this.fireEvent('loadexception', this, o, response, e);
            this.fireEvent('exception', this, 'response', action, o, response, e);
            o.request.callback.call(o.request.scope, null, o.request.arg, false);
            return;
        }
        if (res.success === true) {
            this.fireEvent('load', this, o, o.request.arg);
        } else {
            this.fireEvent('loadexception', this, o, response);
            this.fireEvent('exception', this, 'remote', action, o, res, null);
        }
        o.request.callback.call(o.request.scope, res, o.request.arg, true);
    },
    
    /**
     * Callback for write actions
     * @param {String} action [Ext.data.Api.actions.create|read|update|destroy]
     * @param {Object} trans The request transaction object
     * @param {Object} res The server response
     */
    onWrite: function(action, o, response, rs) {
        var reader = o.reader;
        var res;
        try {
            res = reader.readResponse(action, response);
        } catch (e) {
            this.fireEvent('exception', this, 'response', action, o, response, e);
            o.request.callback.call(o.request.scope, null, o.request.arg, false);
            return;
        }
        if (res.success === true) {
            this.fireEvent('write', this, action, res.data, res, rs, o.request.arg);
        } else {
            this.fireEvent('exception', this, 'remote', action, o, res, rs);
        }
        // TODO refactor onRead, onWrite to be more generalized now that we're dealing with Ext.data.Response instance
        // the calls to request.callback(...) in each will have to be made similar.
        // NOTE reader.readResponse does not currently return Ext.data.Response
        o.request.callback.call(o.request.scope, res.data, res, res.success);
    }
});
